package com.napenalki.toto.service.impl;

import static com.napenalki.toto.service.impl.EntityValidationHelper.validateEntity;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

import com.napenalki.toto.dao.PredictionDao;
import com.napenalki.toto.domain.Event;
import com.napenalki.toto.domain.Match;
import com.napenalki.toto.domain.Prediction;
import com.napenalki.toto.domain.SpecialEvent;
import com.napenalki.toto.service.IllegalEntryException;
import com.napenalki.toto.service.PredictionService;

@Service
public class PredictionServiceImpl implements PredictionService,
        ApplicationContextAware {

    private PredictionDao dao = null;
    private ApplicationContext ctx = null;
    private Map<String, Integer> pointingRules = null;
    private PredictionEvaluator evaluator;

    @Autowired
    public PredictionServiceImpl(PredictionDao dao) {
        this.dao = dao;
    }

    @Override
    public Prediction addPrediction(Prediction prediction) {
        validateEntity(prediction);
        Prediction pred = dao.findPredictionByUserAndEvent(prediction.getUser()
                .getUserName(), prediction.getEvent());
        if (pred == null) {
            return dao.addPrediction(prediction);
        }
        return pred;
    }

    @Override
    public Prediction findPredictionById(int id) {
        return dao.findPredictionById(id);
    }

    @Override
    public List<Prediction> findAllMatchPredictions() {
        return dao.findAllMatchPredictions();
    }

    @Override
    public List<Prediction> findAllSpecialPredictions() {
        return dao.findAllSpecialPredictions();
    }

    @Override
    public List<Prediction> findPredictionsByUser(String userName) {
        if (userName == null) {
            throw new IllegalEntryException("The user should not be null");
        }
        return dao.findPredictionsByUser(userName);
    }

    @Override
    public List<Prediction> findPredictionsByEvent(Event event) {
        if (event == null) {
            throw new IllegalEntryException("The event should not be null");
        }
        return dao.findPredictionsByEvent(event);
    }

    @Override
    public Prediction findPredictionByUserAndEvent(String userName, Event event) {
        if (userName == null) {
            throw new IllegalEntryException("The user should not be null");
        }
        if (event == null) {
            throw new IllegalEntryException("The event should not be null");
        }

        return dao.findPredictionByUserAndEvent(userName, event);
    }

    @Override
    public Prediction updatePrediction(Prediction prediction) {
        validateEntity(prediction);
        return dao.updatePrediction(prediction);
    }

    @Override
    public void deletePrediction(Prediction prediction) {
        if (prediction == null) {
            // TODO add logging
            return;
        }
        dao.deletePrediction(prediction);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.ctx = applicationContext;
    }

    @Override
    public int calculateUserScore(String userName) {
        List<Prediction> predictionsForUser = dao
                .findPredictionsByUser(userName);

        if (evaluator == null) {
            evaluator = new PredictionEvaluator(pointingRules);
        }

        int points = 0;

        for (Prediction prediction : predictionsForUser) {
            if (prediction.getEvent() instanceof Match) {
                points += evaluator.evaluateMatch(prediction);
            } else if (prediction.getEvent() instanceof SpecialEvent) {
                points += evaluator.evaluateSpecialEvent(prediction);
            }
        }

        return points;
    }

    public Map<String, Integer> getPointingRules() {
        return pointingRules;
    }

    public void setPointingRules(Map<String, Integer> pointingRules) {
        this.pointingRules = pointingRules;
    }

    @PostConstruct
    public Map<String, Integer> loadPointingRules() throws IOException {
        Properties rulesProps = new Properties();
        rulesProps.load(ctx.getResource("classpath:META-INF/rules.properties")
                .getInputStream());
        Map<String, Integer> rules = convertProps(rulesProps);
        this.pointingRules = rules;
        return rules;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Integer> convertProps(Properties props) {
        Map<String, Integer> result = new HashMap<String, Integer>();

        Set entrySet = props.entrySet();

        for (Object o : entrySet) {
            Map.Entry<String, String> entry = (Map.Entry<String, String>) o;
            result.put(entry.getKey(), Integer.parseInt(entry.getValue()));
        }
        return result;
    }

    public void setPredictionEvaluator(PredictionEvaluator evaluator) {
        this.evaluator = evaluator;
    }

}
